package com.bac.rentmap.ad.flat.crawler.blingby;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.bac.rentmap.ad.crawler.AdCrawler;
import com.bac.rentmap.ad.crawler.Context;
import com.bac.rentmap.ad.flat.FlatAd;
import com.bac.rentmap.ad.flat.crawler.FlatCrawledAd;
import com.bac.rentmap.jdo.JdoSupport;

public class BlingbyPersistingAdCrawler extends JdoSupport implements
        AdCrawler<FlatCrawledAd> {
    private static final Logger log = Logger.getLogger(BlingbyPersistingAdCrawler.class
            .getName());

    private AdCrawler<FlatCrawledAd> adCrawler;
    
    private int maxCount;
    
    public BlingbyPersistingAdCrawler(AdCrawler<FlatCrawledAd> adCrawler, int maxCount) {
        this.adCrawler = adCrawler;
        this.maxCount = maxCount;
    }

    @Override
    public List<FlatCrawledAd> crawl(Context context) throws IOException {
        List<FlatCrawledAd> flatCrawledAds = adCrawler.crawl(context);
        removeOutdatedFlatAds(flatCrawledAds);

        List<FlatCrawledAd> newFlatCrawledAds = filterNewFlatCrawledAds(flatCrawledAds);
        PersistenceManager persistenceManager = getPersistenceManager();
        try {
            log.fine("Saving " + newFlatCrawledAds.size() + " FlatCrawledAds");
            persistenceManager.makePersistentAll(newFlatCrawledAds);
        } finally {
            persistenceManager.close();
        }
        return newFlatCrawledAds;
    }

    private List<FlatCrawledAd> filterNewFlatCrawledAds(
            List<FlatCrawledAd> flatCrawledAds) {
        List<FlatCrawledAd> newFlatCrawledAds = new ArrayList<FlatCrawledAd>();
        Calendar calendar = GregorianCalendar.getInstance();
        calendar.add(Calendar.DATE, -1); // filter ads from the last 4 days
        Date dateThreshold = calendar.getTime();
        for (FlatCrawledAd flatCrawledAd : flatCrawledAds) {
            if (flatCrawledAd.getPublishingDate().after(dateThreshold)) {
                newFlatCrawledAds.add(flatCrawledAd);
            }
        }
        return newFlatCrawledAds.subList(0, Math.min(maxCount, newFlatCrawledAds.size()));
    }

    private List<FlatAd> loadFlatAds(PersistenceManager persistenceManager) {
        Query query = persistenceManager.newQuery(FlatAd.class);
        String filter = "sourceUrl == sourceUrlParam";
        String queryParameters = "String sourceUrlParam";
        query.setFilter(filter);
        query.declareParameters(queryParameters);
        @SuppressWarnings("unchecked")
        List<FlatAd> res = (List<FlatAd>) query.execute(BlingByAdCrawler.SOURCE_URL);
        log.fine("Loaded " + res.size() + " FlatAds");
        return res;
    }

    private Set<String> collectExternalUrls(List<FlatCrawledAd> flatCrawledAds) {
        Set<String> res = new HashSet<String>();
        for (FlatCrawledAd flatCrawledAd : flatCrawledAds) {
            res.add(flatCrawledAd.getExternalUrl());
        }
        return res;
    }

    private void removeOutdatedFlatAds(List<FlatCrawledAd> flatCrawledAds) {
        Set<String> externalUrls = collectExternalUrls(flatCrawledAds);
        PersistenceManager persistenceManager = getPersistenceManager();
        try {
            List<FlatAd> persistedFlatAds = loadFlatAds(persistenceManager);
            List<FlatAd> outdatedFlatAds = new ArrayList<FlatAd>();
            for (FlatAd flatAd : persistedFlatAds) {
                if (!externalUrls.contains(flatAd.getExternalUrl())) {
                    outdatedFlatAds.add(flatAd);
                }
            }
            log.fine("Removing " + outdatedFlatAds.size() + " outdated FlatAds");
            persistenceManager.deletePersistentAll(outdatedFlatAds);
        } finally {
            persistenceManager.close();
        }
    }
}
